/**
 * Copyright (C) 2025 pony working team
 * SPDX-License-Identifier: MulanPSL-2.0
 */

/* includes (local) ----------------------------------------------------------*/
#include <ui.h>
/* includes (standard library, system) ---------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
/* includes (other library) --------------------------------------------------*/
#include <lv_main.h>
/* includes (project) --------------------------------------------------------*/
#include <bsp_lcd.h>

#include <hal_i2c.h>
#include <pony/tp/tp.h>

#include <k_tick.h>

/* defines -------------------------------------------------------------------*/

#define DISPLAY_BUF_SIZE (CONFIG_BYTE_PER_PIXEL * LCD_HOR_RES * LCD_VER_RES)

/* typedefs ------------------------------------------------------------------*/
/* macro ---------------------------------------------------------------------*/
/* variables (extern) --------------------------------------------------------*/
/* variables (local) ---------------------------------------------------------*/

static uint8_t *__disp_buf = NULL;

/* variables (global) --------------------------------------------------------*/
/* inlines -------------------------------------------------------------------*/
/* functions -----------------------------------------------------------------*/

static void my_flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map)
{
    uint16_t *buf16 = (uint16_t *)px_map; /* Let's say it's a 16 bit (RGB565) display */
    int32_t x, y;

    for (y = area->y1; y <= area->y2; y++) {
        for (x = area->x1; x <= area->x2; x++) {
            bsp_lcd_setpixel(x, y, *buf16);
            buf16++;
        }
    }

    /* Inform LVGL that you are ready with the flushing and buf is not used anymore */
    lv_display_flush_ready(disp);
}

static void my_input_read(lv_indev_t *indev, lv_indev_data_t *data)
{
    touch_point_t point = {0};

    tp_get_point(&point);

    if (point.state) {
        data->point.x = point.x;
        data->point.y = point.y;
        data->state = LV_INDEV_STATE_PRESSED;
    } else {
        data->state = LV_INDEV_STATE_RELEASED;
    }
}

static void event_handler(lv_event_t *e)
{
    lv_event_code_t code = lv_event_get_code(e);

    if (code == LV_EVENT_CLICKED) {
        LV_LOG_USER("Clicked");
    } else if (code == LV_EVENT_VALUE_CHANGED) {
        LV_LOG_USER("Toggled");
    }
}

static void lv_example_button_1(void)
{
    lv_obj_t *label;

    lv_obj_t *btn1 = lv_button_create(lv_screen_active());
    lv_obj_add_event_cb(btn1, event_handler, LV_EVENT_ALL, NULL);
    lv_obj_align(btn1, LV_ALIGN_CENTER, 0, -40);
    lv_obj_remove_flag(btn1, LV_OBJ_FLAG_PRESS_LOCK);

    label = lv_label_create(btn1);
    lv_label_set_text(label, "Button");
    lv_obj_center(label);

    lv_obj_t *btn2 = lv_button_create(lv_screen_active());
    lv_obj_add_event_cb(btn2, event_handler, LV_EVENT_ALL, NULL);
    lv_obj_align(btn2, LV_ALIGN_CENTER, 0, 40);
    lv_obj_add_flag(btn2, LV_OBJ_FLAG_CHECKABLE);
    lv_obj_set_height(btn2, LV_SIZE_CONTENT);

    label = lv_label_create(btn2);
    lv_label_set_text(label, "Toggle");
    lv_obj_center(label);
}

__weak void lv_main(void)
{
    lv_example_button_1();
}

static int ui_main_task(uintptr_t arg)
{
    lv_tick_set_cb(tick_ms);

    lv_display_t *disp = lv_display_create(LCD_HOR_RES, LCD_HOR_RES);
    lv_display_set_flush_cb(disp, my_flush_cb);
    lv_display_set_buffers(disp, __disp_buf, NULL, DISPLAY_BUF_SIZE,
                           LV_DISPLAY_RENDER_MODE_PARTIAL);

    /* Register at least one display before you register any input devices */
    lv_indev_t *indev = lv_indev_create();
    lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
    lv_indev_set_read_cb(indev, my_input_read);

    // Change the active screen's background color
    lv_obj_t *scr = lv_screen_active();
    lv_obj_set_style_bg_color(scr, lv_color_black(), LV_PART_MAIN);
    lv_obj_set_style_bg_opa(scr, LV_OPA_100, LV_PART_MAIN);
    lv_obj_set_style_text_color(scr, lv_color_hex(0xffffff), LV_PART_MAIN);

    lv_main();

    for (;;) {
        uint32_t time_till_next = lv_timer_handler();
        task_msleep(time_till_next);
    }
    return 0;
}

static int ui_tick_task(uintptr_t arg)
{
    for (;;) {
        lv_tick_inc(10);
        task_msleep(10);
    }
    return 0;
}

int ui_init(void)
{
    __disp_buf = (uint8_t *)malloc(DISPLAY_BUF_SIZE);
    if (__disp_buf == NULL) {
        return -1;
    }

    lv_init();

    if (hal_i2c_dev_open("st1633") == NULL) {
        return -2;
    }

    task_spawn("ui_tick", 10, 0, 0x100, ui_tick_task, 0);
    task_spawn("ui_main", 11, 0, 0x2000, ui_main_task, 0);

    return 0;
}
